package org.dhy.elasticsearch.processer;

import lombok.*;
import org.dhy.elasticsearch.annotate.Document;
import org.dhy.elasticsearch.annotate.EsField;
import org.dhy.elasticsearch.annotate.Id;
import org.dhy.elasticsearch.annotate.SortBy;
import org.dhy.elasticsearch.enums.Logical;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

/**
 * @author longye
 */
@Getter(AccessLevel.PUBLIC)
public class EsEntityProcesser<T> {
    private String idField;

    private Index index;

    private List<SearchField> esFields;

    private Sort sort;

    private XContentBuilder xContentBuilder;

    private Class<T> realClass;

    public EsEntityProcesser(Class<T> realClass) {
        this.realClass = realClass;
        init();
    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class Index {
        private String name;
        private Integer shards;
        private Integer replicas;

        public static boolean checkIndexNoNull(Index index) {
            return index != null && !StringUtils.isEmpty(index.name);
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class SearchField {
        String fieldName;
        Boolean searchAble;
        String analyzer;
        String searchAnalyzer;
        Class type;
        Logical logical;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class Sort {
        String fieldName;
        SortOrder sortOrder;
    }

    private void init() {
        if (this.esFields == null) {
            this.esFields = new ArrayList<>();
        }

        Class<T> tClass = this.realClass;
        boolean documentClass = tClass.isAnnotationPresent(Document.class);

        if (!documentClass) {
            throw new IllegalArgumentException("本类没有Document注解");
        }

        Document document = tClass.getAnnotation(Document.class);
        this.index = Index.builder()
                .name(document.index())
                .replicas(document.replicas())
                .shards(document.shards())
                .build();

        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
            boolean fieldHasAnno = field.isAnnotationPresent(EsField.class);
            if (fieldHasAnno) {
                EsField fieldAnno = field.getAnnotation(EsField.class);
                String analyzer = fieldAnno.analyzer();
                boolean searchable = fieldAnno.searchable();
                final String searchAnalyzer = fieldAnno.searchAnalyzer();
                final Class<?> type = field.getType();
                Logical logical = fieldAnno.logical();
                SearchField searchField = SearchField.builder()
                        .analyzer(analyzer)
                        .fieldName(field.getName())
                        .searchAble(searchable)
                        .searchAnalyzer(searchAnalyzer)
                        .type(type)
                        .logical(logical)
                        .build();
                this.esFields.add(searchField);
            }

            boolean idAnnotationPresent = field.isAnnotationPresent(Id.class);
            if (idAnnotationPresent) {
                this.idField = field.getName();
            }

            final boolean annotationPresent = field.isAnnotationPresent(SortBy.class);
            if (annotationPresent) {
                final SortBy annotation = field.getAnnotation(SortBy.class);
                final SortOrder order = annotation.order();
                this.sort = Sort.builder().sortOrder(order)
                        .fieldName(field.getName())
                        .build();
            }
        }
    }

    public XContentBuilder generateBuilder() throws IOException {
        if (this.xContentBuilder != null) {
            return this.xContentBuilder;
        }

        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();

        builder.startObject("properties");
        for (SearchField searchField : esFields) {
            builder.startObject(searchField.fieldName);
            if (searchField.type.equals(String.class)) {
                builder.field("type", "text");
                if (searchField.analyzer != null) {
                    builder.field("analyzer", searchField.analyzer);
                    builder.field("search_analyzer", searchField.searchAnalyzer);
                }
            }
            builder.endObject();
        }
        builder.endObject();
        builder.endObject();
        this.xContentBuilder = builder;
        return builder;
    }
}
